package com.zyht.redis.service.impl;

import java.lang.annotation.Annotation;

import javax.annotation.Resource;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import com.wx.util.JsonUtil;
import com.zyht.redis.CacheExpire;
import com.zyht.redis.CacheKey;
import com.zyht.redis.Cacheable;
import com.zyht.redis.Cacheclean;
import com.zyht.redis.service.RedisService;

/**
 * 注解缓存
 * 
 * @author dingyaming
 *
 */
@Aspect
@Component
public class CacheAop {
	@Resource
	private RedisService redisService;

	@Around(value = "@annotation(cache)")
	public Object cache(final ProceedingJoinPoint pjp, Cacheable cache) throws Throwable {
		String key = getCacheKey(pjp, cache);
		if (key == null || key.isEmpty()) {
			throw new RuntimeException("缺少 @CacheKey或 @Cacheable.key");
		}
		String value = redisService.get(key);
		if (value != null) {// 如果有数据,则直接返回
			Class returnType = ((MethodSignature) pjp.getSignature()).getMethod().getReturnType();
			return JsonUtil.parseObject(value, returnType);
		}
		Object res = pjp.proceed(); // 跳过缓存,到后端查询数据
		redisService.set(key, JsonUtil.toJSONString(res));
		if (cache.expire() <= 0) { // 如果没有设置过期时间,则无限期缓存
			Long expire = getCacheKeyExpire(pjp);
			if (expire == null || expire <= 0) {
			} else {
				redisService.expire(key, expire);
			}
		} else { // 否则设置缓存时间
			redisService.expire(key, cache.expire());
		}
		return res;
	}

	/**
	 * 获取缓存的key值
	 * 
	 * @param pjp
	 * @param cache
	 * @return
	 */
	private String getCacheKey(ProceedingJoinPoint pjp) {
		Annotation[][] pas = ((MethodSignature) pjp.getSignature()).getMethod().getParameterAnnotations();
		Object[] args = pjp.getArgs();
		for (int i = 0; i < pas.length; i++) {
			for (Annotation an : pas[i]) {
				if (an instanceof CacheKey) {
					Object object = args[i];
					if (object == null) {
						throw new RuntimeException("@CacheKey 标注的参数不能为 null");
					}
					if (object instanceof String) {
					} else {
						throw new RuntimeException("@CacheKey 必须标注在 String类型参数上");
					}
					return (String) object;
				}
			}
		}
		throw new RuntimeException("缺少 @CacheKey 标注的参数");
	}

	/**
	 * 
	 * <blockquote>
	 * 
	 * <pre>
	 * 
	 * </pre>
	 * 
	 * </blockquote>
	 * 
	 * @param pjp
	 * @return 返回 long 有效期, 默认值 0
	 */
	private Long getCacheKeyExpire(ProceedingJoinPoint pjp) {
		Annotation[][] pas = ((MethodSignature) pjp.getSignature()).getMethod().getParameterAnnotations();
		Object[] args = pjp.getArgs();
		for (int i = 0; i < pas.length; i++) {
			for (Annotation an : pas[i]) {
				if (an instanceof CacheExpire) {
					Object object = args[i];
					if (object == null) {
						throw new RuntimeException("@CacheExpire 标注的参数不能为 null");
					}
					if (object instanceof Long) {
					} else {
						throw new RuntimeException("@CacheExpire 必须标注在 Long类型参数上");
					}
					Long expire = (Long) object;
					if (expire >= 0) {
						return expire;
					} else {
						throw new RuntimeException("@CacheExpire 标注的参数值>=0");
					}
				}
			}
		}
		return 0L;
	}

	/**
	 * 获取缓存的key值
	 * 
	 * @param pjp
	 * @param cache
	 * @return
	 */
	private String getCacheKey(ProceedingJoinPoint pjp, Cacheable cache) {
		if (cache.key().isEmpty()) {
			return getCacheKey(pjp);
		} else {
			return (cache.key());
		}
	}

	/**
	 * 获取缓存的key值
	 * 
	 * @param pjp
	 * @param cache
	 * @return
	 */
	private String getCacheKey(ProceedingJoinPoint pjp, Cacheclean cache) {
		if (cache.key().isEmpty()) {
			return getCacheKey(pjp);
		} else {
			return (cache.key());
		}
	}

	@Around(value = "@annotation(cache)")
	public Object cacheClean(final ProceedingJoinPoint pjp, Cacheclean cache) throws Throwable {
		String key = getCacheKey(pjp, cache);
		if (key == null || key.isEmpty()) {
			throw new RuntimeException("缺少 @CacheKey 或 @Cacheable.key");
		}
		redisService.del(key);
		return pjp.proceed(); // 执行
	}
}